/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package Splines;

import java.util.ArrayList;
import java.util.Arrays;
import processing.core.PApplet;
// la libreria proscene
import remixlab.proscene.*;
/*
 *
 * @author Jhon
 */
public class Splines extends PApplet{

    
    ArrayList<Points> points;
    ArrayList<InteractiveFrame> frameA;
    Points ref;
    boolean drawP;
    int sizeR;
    boolean isOver;
    //pintar
    boolean line;
    boolean splineCubic;
    boolean box;
    boolean bezzier;
    boolean cardinal;
    float tension;
    Scene scene;
    static final int MAX = 300;
    static long e[][] = new long[MAX][MAX];
    float tCardinal;
    public void setup() {
        size(640, 360,P3D);
        points=new ArrayList<Points>();
        drawP=false;
        sizeR=10;
        line=false;
        tCardinal=0;
        frameA = new ArrayList<InteractiveFrame>();
        scene = new Scene(this);
        scene.setShortcut('f', Scene.KeyboardAction.DRAW_FRAME_SELECTION_HINT);
        splineCubic=false;
        cardinal=false;
        box=true;
        bezzier=false;
        tension = 0.333f;
        for(int i= 0;i<MAX;i++){
            Arrays.fill(e[i], -1);
        }

            
        
    }

     static long c( int N , int K)
    {
        if( K == 0 || K == N )return 1;
        if( K < 0 || K > N )return 0;
        if( e[N][K] != -1 ) return e[N][K];
        return e[N][K] = c( N-1 , K ) + c( N-1 , K-1 );
    }

    public void draw() {
       
        

  
        scene.drawGrid();
        scene.drawAxis();
        stroke(255,0,0);
        strokeWeight(1);


        if(box){
        if(!points.isEmpty()){
            int i = 0;
            for(Points p : points){

                //ACA es lo q le digo lo de esa monda de pushMatrix...
                //cuando le doy pushMatrix y hago una matriz de transformacion todo lo q este contenido le aplica esa
                //matriz de  transformacion
                pushMatrix();
                frameA.get(i).applyTransformation();
                p.setX((int)frameA.get(i).translation().x);
                p.setY((int)frameA.get(i).translation().y);
                p.setZ((int)frameA.get(i).translation().z);
                //aca es donde mira si el mouse esta parado sobre el box
                if(frameA.get(i).grabsMouse())
                fill(255,0,0);
                else
                fill(100,0,0);
                //aca pues imprime el box.... como pilla no toca darle coordenadas
                //porque como esta el pushmatrix todo lo q imprima aca le aplica esa matriz de transformacion
                
                popMatrix();
                i++;
            }
        }
        
        }


        strokeWeight(2);
        
        if(line){
            stroke(255,255,0);
            if(!points.isEmpty()){
                if(points.size()>=2){
                    for(int i=1;i<points.size();i++){
                        line(points.get(i-1).getX(),points.get(i-1).getY(),points.get(i-1).getZ(),points.get(i).getX(),points.get(i).getY(),points.get(i).getZ());
                    }
                }
            }
            
        }


        strokeWeight(3);
        
        if(splineCubic){
            stroke(0,255,255);
            if(!points.isEmpty()){
                if(points.size()>=3){
                    for(int i=0;i<points.size()-1;i++){
                        if(i==0){
                        SplineCubicI(null,points.get(i),points.get(i+1),points.get(i+2));
                        }else if(i==points.size()-2){
                        SplineCubicI(points.get(i-1),points.get(i),points.get(i+1),null);
                        break;
                        }else{
                        SplineCubicI(points.get(i-1),points.get(i),points.get(i+1),points.get(i+2));    
                            
                        }
                    }
                    
                }
            }

        }

        /////CARDINAL
        if(cardinal){
            stroke(0,0,255);
            if(!points.isEmpty()){
                if(points.size()>=4){
                    for(int i=0;i<points.size()-1;i++){
                        if(i==0){
                        SplineCardinal(null,points.get(i),points.get(i+1),points.get(i+2));
                        }else if(i==points.size()-2){
                        SplineCardinal(points.get(i-1),points.get(i),points.get(i+1),null);
                         break;
                        }else{
                        SplineCardinal(points.get(i-1),points.get(i),points.get(i+1),points.get(i+2));
                        }
                    }

                }
            }

        }


        strokeWeight(2);
        stroke(255,0,255);
        if(bezzier){

            if(!points.isEmpty()){
                if(points.size()>=2){
                SplineBezier();
                }
            }
            
        }




    }




    public void keyPressed() {
        if(key=='p'){

             points.add(new Points((int)scene.camera().position().x,(int)scene.camera().position().y,(int)scene.camera().position().z));
             InteractiveFrame f = new InteractiveFrame(scene);
             //aca es donde agrega los interactive frame.. rebreve simplemente le digo cual es la traslacion y sale q es la misma posicion del punto de control...
             f.setTranslation((int)scene.camera().position().x,(int)scene.camera().position().y,(int)scene.camera().position().z);
             frameA.add(f);
             
        }else if(key=='l'){
            line=!line;
        }else if(key=='s'){
            splineCubic=!splineCubic;
        }else if(key=='b'){
            box=!box;
        }else if(key=='z'){
            bezzier=!bezzier;
        }else if(key=='a'){
            cardinal=!cardinal;
        }else if(key=='c'){
            
            line=false;
            points.clear();
            frameA.clear();
        
        }else if(key=='t'){
            if(tCardinal>0)
            tCardinal-=0.01;
            System.out.println(tCardinal);
        }else if(key=='y'){
            if(tCardinal<1)
            tCardinal+=0.01;
            System.out.println(tCardinal);

        }
        else{
        }
    }


    public boolean isIn(int mX,int mY,int rX,int rY){
        if(mX<=rX+sizeR && mX>=rX && mY<=rY+sizeR && mY>=rY)
            return true;
        else
            return false;
    }
    



    public void SplineBezier(){
        float t = 0,PX,PY,PZ;
        int n = points.size();
        while(t<=1){
            PX=PY=PZ=0;
            for(int i=0;i<n;i++){
                PX+=c(n-1,i)*points.get(i).getX()*Math.pow((1-t), n-1-i)*Math.pow(t, i);
                PY+=c(n-1,i)*points.get(i).getY()*Math.pow((1-t), n-1-i)*Math.pow(t, i);
                PZ+=c(n-1,i)*points.get(i).getZ()*Math.pow((1-t), n-1-i)*Math.pow(t, i);
            }
            point(PX,PY,PZ);
            t+=0.001;
        }



    }

// http://www.disi.unal.edu.co/grupos/remixlab/index.php?option=com_jumi&fileid=9&Itemid=18&lang=es
   public void SplineCardinal(Points kmenos1,Points k, Points kmas1,Points kmas2){
       float t = 0,s=(tCardinal),h1,h2,h3,h4,h21,h31,PX1,PY1,PZ1,PX0,PY0,PZ0;
       while(t<=1){
       h1=((-(t*t*t))+(2*(t*t))-(t))*s;
       h2=((-(t*t*t))+((t*t)))*s;
       h21=((2*(t*t*t))-(3*(t*t))+1);
       h3=((t*t*t)-(2*(t*t))+(t))*s;
       h31=((-2*(t*t*t))+(3*(t*t)));
       h4=((t*t*t)-(t*t))*s;
       if(kmenos1!=null && kmas2!=null){
       PX1=kmenos1.getX()*h1 + k.getX()*h2 +k.getX()*h21 + kmas1.getX()*h3 +kmas1.getX()*h31 + kmas2.getX()*h4;
       PY1=kmenos1.getY()*h1 + k.getY()*h2 + kmas1.getY()*h3 +k.getY()*h21 + kmas1.getY()*h31 + kmas2.getY()*h4;
       PZ1=kmenos1.getZ()*h1 + k.getZ()*h2 + kmas1.getZ()*h3 +k.getZ()*h21 + kmas1.getZ()*h31 + kmas2.getZ()*h4;
       point(PX1,PY1,PZ1);

       }else if(kmenos1!=null){
       PX1=kmenos1.getX()*h1 + k.getX()*h2 +k.getX()*h21 + kmas1.getX()*h3 +kmas1.getX()*h31;
       PY1=kmenos1.getY()*h1 + k.getY()*h2 + kmas1.getY()*h3 +k.getY()*h21 + kmas1.getY()*h31;
       PZ1=kmenos1.getZ()*h1 + k.getZ()*h2 + kmas1.getZ()*h3 +k.getZ()*h21 + kmas1.getZ()*h31;
       point(PX1,PY1,PZ1);

       }else if(kmas2!=null){
        PX1=k.getX()*h2 +k.getX()*h21 + kmas1.getX()*h3 +kmas1.getX()*h31 + kmas2.getX()*h4;
       PY1= k.getY()*h2 + kmas1.getY()*h3 +k.getY()*h21 + kmas1.getY()*h31 + kmas2.getY()*h4;
       PZ1= k.getZ()*h2 + kmas1.getZ()*h3 +k.getZ()*h21 + kmas1.getZ()*h31 + kmas2.getZ()*h4;
       point(PX1,PY1,PZ1);

       }



        t+=0.005;
       }
   }






    public void SplineCubicI(Points before, Points init, Points end,Points after){
        float t = 0,h00,h01,h10,h11,px0,px1,mx0,mx1
                ,py0,py1,my0,my1,pz0,pz1,mz0,mz1,PX,PY,PZ,PX1,PY1,PZ1;
         px0=init.getX();
        py0=init.getY();
        pz0=init.getZ();
        px1=end.getX();
        py1=end.getY();
        pz1=end.getZ();

        if(before!=null && after!=null){

        mx0=((end.getX()-init.getX())/2)+((init.getX()-before.getX())/2);
        my0=((end.getY()-init.getY())/2)+((init.getY()-before.getY())/2);
        mz0=((end.getZ()-init.getZ())/2)+((init.getZ()-before.getZ())/2);
        mx1=((after.getX()-end.getX())/2)+((end.getX()-init.getX())/2);
        my1=((after.getY()-end.getY())/2)+((end.getY()-init.getY())/2);
        mz1=((after.getZ()-end.getZ())/2)+((end.getZ()-init.getZ())/2);
        }else if(before!=null){
            px0=init.getX();
        mx0=((end.getX()-init.getX())/2)+((init.getX()-before.getX())/2);
        my0=((end.getY()-init.getY())/2)+((init.getY()-before.getY())/2);
        mz0=((end.getZ()-init.getZ())/2)+((init.getZ()-before.getZ())/2);
        mx1=0;
        my1=0;
        mz1=0;
        }else{
         mx0=0;
        my0=0;
        mz0=0;
        mx1=((after.getX()-end.getX())/2)+((end.getX()-init.getX())/2);
        my1=((after.getY()-end.getY())/2)+((end.getY()-init.getY())/2);
        mz1=((after.getZ()-end.getZ())/2)+((end.getZ()-init.getZ())/2);
        }

        h00=(2*(t*t*t))-(3*(t*t))+1;
        h10=(t*t*t)-(2*(t*t))+t;
        h01=-(2*(t*t*t))+(3*(t*t));
        h11=(t*t*t)-(t*t);
        PX1=(h00*px0)+(h10*mx0)+(h01*px1)+(h11*mx1);
        PY1=(h00*py0)+(h10*my0)+(h01*py1)+(h11*my1);
        PZ1=(h00*pz0)+(h10*mz0)+(h01*pz1)+(h11*mz1);
        point(PX1,PY1,PZ1);

        t+=0.02;
        while(t<=1){
             h00=(2*(t*t*t))-(3*(t*t))+1;
            h10=(t*t*t)-(2*(t*t))+t;
            h01=-(2*(t*t*t))+(3*(t*t));
            h11=(t*t*t)-(t*t);
            PX=(h00*px0)+(h10*mx0)+(h01*px1)+(h11*mx1);
            PY=(h00*py0)+(h10*my0)+(h01*py1)+(h11*my1);
            PZ=(h00*pz0)+(h10*mz0)+(h01*pz1)+(h11*mz1);
            line(PX1,PY1,PZ1,PX,PY,PZ);
            t+=0.02;
            PX1=PX;
            PY1=PY;
            PZ1=PZ;
        }
    }

    


       public static void main(String[] args) {
        PApplet.main(new String[]{"Splines.Splines"});
    }
}
